/*
 * html-camera javascript plugin
 * License: MIT (http://www.opensource.org/licenses/mit-license.php)
 * 依赖html2canvas(https://github.com/niklasvh/html2canvas)，使用细节可以查看其官方文档
 * http://html2canvas.hertzen.com/documentation
 * 兼容性：
 * Firefox 3.5+
 * Google Chrome
 * Opera 12+
 * IE9+
 * Edge
 * Safari 6+
 * 
 * 不支持iframe
 * 不能在浏览器插件中使用
 * 不支持Flash
 * 不支持古代浏览器
*/

/**
 * 处理跨域图片：canvas对于图片资源有同源限制，需要图片资源响应头设置 Access-Control-Allow-Origin，且设置参数html2canvasOptions : {useCORS: true}
 * 需要引入html2canvas,并保证window.Promise可用，不存在时，var htmlCamera = require('html-camera')(Promise);
 * 在支持的打印内容中，打印效果“所见即所打”，无法根据打印纸的大小，自适应打印效果
 * 常用样式中，border-style只支持solid，由html2canvas决定
 */

(function (global, factory) {
  var P = global.Promise;
  if (typeof module === "object" && typeof module.exports === "object") {
    var html2canvas = require('html2canvas');
    module.exports = typeof P === 'function' ? factory(global, html2canvas, P) : function (P) {
      if (typeof p !== 'function') {
        throw new Error('html-camera : 传入的Promise不可用');
      }
      return factory(global, html2canvas, P);
    };
  } else {
    factory(global, global['html2canvas'], P);
  }
})(typeof window !== "undefined" ? window : this, function (global, html2canvas, Promise) {
  if (typeof html2canvas !== 'function') {
    throw new Error('html-camera : 依赖项html2canvas不可用');
  }
  if (typeof Promise !== 'function') {
    throw new Error('html-camera : Promise不可用');
  }

  var doc = global.document,
    theBody = doc.body;

  var defaultConfig = {
    imgType: 'image/png'
  };

  function htmlCamera(doms, opt) {
    return new HtmlCamera(doms, opt);
  }

  /**
   * 
   * @param {dom元素} doms 
   *  {var} 支持dom，一维dom数组，二维dom数组，三维dom数组
   *    传dom或一维数组时，直接打印；
   *    传二维数组时，数组的第二维度会组合成在一起，以保证其不会分开打印；
   *    传三维数组时，数组的第三维度中的元素，会尽量显示在一行
   * @param {obj} opt 
   *  {fn} getImgs dom转图片后回调 return false时，中断后续
   *  {fn} getDataURLs dom转图片base64后回调 return false时，中断后续
   *  {array} split 插入打印分页符的一维dom数组的索引集合
   *  {obj} html2canvasOptions 传html2canvas的参数，详情参考 http://html2canvas.hertzen.com/configuration
   *  {bool} isPrint 是否打印 default: true 
   *  {bool} isDebug 是否进入调试模式 defualt: false 是否保留中间过程在页面中生成的元素
   *  {bool} error异常时回调
   */
  function HtmlCamera(doms, opt) {
    opt = opt || {};
    var me = this;
    me.iframes = [];//已经生成的iframes;
    me.doms = doms;//要打印的DOM
    me.imgType = opt.imgType || defaultConfig.imgType; //生的图片类型
    var getImgs = me.getImgs = opt.getImgs; //获取生成的图片的回调
    var getDataURLs = me.getDataURLs = opt.getDataURLs; //获取生成的图片base64的回调
    var isPrint = me.isPrint = opt.isPrint !== false;//是否要打印

    me.html2canvasOptions = opt.html2canvasOptions || {}; //核心依赖html2canvas的option
    me.error = opt.error; //异常时的回调，一般只用于调试
    me.isDebug = opt.isDebug; //是否启用调试，一般只用于调试

    me.getCanvasByDom({
      doms: doms,
      success: function (aCanvas) {
        var isStop = true;
        var aDataUrl = me.getDataUrlByCanvas(aCanvas);

        isStop = getDataURLs && getDataURLs.call(me, aDataUrl) === false;

        if (!isStop) {
          var aImg = me.getImgByDataUrl(aDataUrl, false, opt.split);
          isStop = getImgs && getImgs(aImg) === false;
          isStop || (isPrint && me.printImgs(aImg));
        }
      }
    });
  }

  HtmlCamera.prototype.getCanvasByDom = function (opt) {
    var me = this;
    var isDebug = me.isDebug;
    var error = me.error;
    var html2canvasOptions = me.html2canvasOptions;
    var doms = opt.doms;
    var success = opt.success;
    //doms数组的维度
    var level = opt.level || 0;
    Array.isArray(doms) || (doms = [doms]);

    var proArr = [];
    var canArr = [];
    for (var i = 0; i < doms.length; i++) {
      (function (index) {
        var item = doms[index];
        if (Array.isArray(item)) {
          switch (level) {
            case 0:
            case 1:
              proArr.push(new Promise(function (resolve, reject) {
                me.getCanvasByDom({
                  level: level + 1,
                  doms: item,
                  success: function (aCanvas) {
                    var aDataUrl = me.getDataUrlByCanvas(aCanvas);
                    var aImg = me.getImgByDataUrl(aDataUrl, level == 1);

                    var outerDiv = createElement('div');
                    var oDiv = createElement('div');
                    //让其不可见且不影响页面布局.
                    setCssText(outerDiv, 'height:0;opacity:0;position:fixed;z-index:-10;left:0;top:0;');
                    //让其被子元素撑开且不受空格影响
                    setCssText(oDiv, 'display:inline-block;font-size:0;');

                    for (var i = 0; i < aImg.length; i++) {
                      var oImg = aImg[i];
                      appendChild(oDiv, oImg);
                    }
                    appendChild(outerDiv, oDiv);
                    appendChild(theBody, outerDiv);

                    me.getCanvasByDom({
                      level: 0,
                      doms: oDiv,
                      success: function (aCanvas) {
                        canArr[index] = aCanvas[0];
                        theBody.removeChild(outerDiv);
                        resolve();
                      }
                    });
                  },
                })
              }));
              break;
          }
        } else {
          proArr.push(html2canvas(item, html2canvasOptions).then(function (canvas) {
            isDebug && document.body.appendChild(canvas);
            return canArr[index] = canvas;
          }).catch(function () {
            canArr[index] = createElement('canvas');
            error && error(i, item, doms);
          }));
        }
      })(i);
    }
    return Promise.all(proArr).then(function () {
      success(canArr);
      return canArr;
    });
  };

  HtmlCamera.prototype.getDataUrlByCanvas = function (aCanvas) {
    var type = this.imgType;
    var arr = [];
    for (var i = 0; i < aCanvas.length; i++) {
      arr.push(aCanvas[i].toDataURL(type));
    }
    return arr;
  };

  HtmlCamera.prototype.getImgByDataUrl = function (aDataUrl, isInlineBlock, split) {
    split = split || [];
    var arr = [];
    var cssText = 'display:' + (isInlineBlock ? 'inline-' : '') + 'block;border:none;';
    cssText += 'max-width: 100%;'
    for (var i = 0; i < aDataUrl.length; i++) {
      var oImg = createElement('img');
      setCssText(oImg, cssText);
      oImg.src = aDataUrl[i];
      split.indexOf(i) > -1 && (oImg.style.pageBreakAfter = 'always');
      arr.push(oImg);
    }
    return arr;
  };

  HtmlCamera.prototype.printImgs = function (imgs) {
    var isDebug = this.isDebug;
    var id = getId();
    var ifr = createElement('iframe');
    var oStyle = createElement('style');
    var cssId = '#' + id;
    var cssText = cssId + '{';
    cssText += isDebug ? 'width:100%;height:100%;' : 'position:absolute;top:0;left:0;width:0;height:0;border:0;z-index:-10;';
    cssText += '}';
    cssText += '@media print{ ' + cssId + ' {width:100%;height:100%;z-index:10000000} }';

    oStyle.innerText = cssText;
    appendChild(doc.head, oStyle);

    ifr.id = id;
    ifr.onload = function () {
      var oDiv = createElement('div');
      for (var i = 0; i < imgs.length; i++) {
        appendChild(oDiv, imgs[i]);
      }
      var contentWindow = ifr.contentWindow;
      appendChild(contentWindow.document.body, oDiv);
      setTimeout(function () {
        if (!isDebug) {
          setTimeout(function () {
            theBody.removeChild(ifr);
            doc.head.removeChild(oStyle);
          }, 100);
        }
        contentWindow.focus();
        contentWindow.print();
      }, 200);
    };

    appendChild(theBody, ifr);

    //iframe里的样式
    var ifrStyle = createElement('style');
    var ifrCssText = 'body{margin: 0;padding: 0}';
    ifrStyle.innerText = ifrCssText;
    appendChild(ifr.contentWindow.document.head, ifrStyle);
  };

  function createElement(tag) {
    return doc.createElement(tag)
  }

  function appendChild(parent, son) {
    parent.appendChild(son);
  }

  function setCssText(dom, cssText) {
    dom.style.cssText = cssText;
  }

  function getId() {
    return 'ifm' + (Math.random() + '').replace('.', '');
  }

  if (typeof define === "function") {//amd cmd
    define('html-camera', [], function () {
      return htmlCamera;
    });
  } else {
    global['htmlCamera'] = htmlCamera;
  }

  return htmlCamera;
});